-----------The Serpent's Star----------
A 4am crack                  2017-08-24
---------------------------------------

Name: The Serpent's Star
Version: 1.0
Genre: adventure
Year: 1983
Credits: Ultrasoft
Publisher: Broderbund Software
Platform: Apple ][+ or later
Media: double-sided 5.25-inch floppy
OS: DOS 3.3

Only side A is bootable. (Technically,
side B is bootable in the sense that it
boots to a message that says "NO D.O.S.
ON THIS DISK." Let's not be pedantic.)

(Also, "D.O.S." looks strange to my
modern eyes, but it's not any less
correct than "DOS" without the periods
between each letter.)

(Fun fact: "DOS" is an acronym, not an
initialism. Acronyms are spoken as if
they were a word, like "ASCII," while
initialisms are pronounced letter by
letter, like "DVD.")

(I am not fun at parties.)

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA (side A)
  read error on first pass

Locksmith Fast Disk Backup
  unable to read track $03 or above

EDD 4 bit copy (no sync, no count)
  works

Copy ][+ nibble editor
  no obvious signs of why track $03+
  would be unreadable

Disk Fixer
  T00,S00 looks like a standard DOS 3.3
    bootloader
  in fact, tracks 0-2 look like a
    standard copy of DOS 3.3
  T01,S09 -> startup program is "START"

Why didn't COPYA work?
  something funky going on on track 3+

Why didn't Locksmith FDB work?
  ditto

EDD worked. What does that tell us?
  No half or quarter tracks, because I
  didn't even try to copy those. Just
  the structural changes (prologues and
  epilogues) and an RWTS patched to
  read them.

Next steps:

  1. Convert disk to standard format
     with Advanced Demuffin
  2. Patch RWTS to read standard format
  3. Declare victory (*)

(*) Go to the gym

                   ~

               Chapter 1
        In Which We Attempt To
          Intercept The Note


Turning to my trusty Disk Fixer sector
editor, I can examine the bootloader to
see if anything stands out.

T00,S00 is identical to an unprotected
DOS 3.3 disk.

T00,S01, loaded at $B700, is usually
responsible for loading the rest of DOS
from tracks 2, 1, and 0, then jumping
to $9D84 to initialize DOS and load the
startup program.

Looking at the code, everything appears
to be in order, until the final jump:

                 --v--

T00,S01
----------- DISASSEMBLY MODE ----------
002E:4A             LSR
002F:AA             TAX
0030:A9 00          LDA   #$00
0032:9D F8 04       STA   $04F8,X
0035:9D 78 04       STA   $0478,X

; read DOS from disk
0038:20 93 B7       JSR   $B793

; reset stack
003B:A2 FF          LDX   #$FF
003D:9A             TXS
003E:8E EB B7       STX   $B7EB

; machine initialization
0041:20 93 FE       JSR   $FE93
0044:20 89 FE       JSR   $FE89

; jump to... wait, what?
0047:4C B5 BE       JMP   $BEB5

                 --^--

$BE00 is loaded from track 0, sector 8.

                 --v--

T00,S08
----------- DISASSEMBLY MODE ----------
; munge reset vector
00B5:A9 00          LDA   #$00
00B7:8D F4 03       STA   $03F4

; clear hi-res screen
00BA:A2 20          LDX   #$20
00BC:85 44          STA   $44
00BE:86 45          STX   $45
00C0:A2 40          LDX   #$40
00C2:A8             TAY
00C3:91 44          STA   ($44),Y
00C5:C8             INY
00C6:D0 FB          BNE   $00C3
00C8:E6 45          INC   $45
00CA:CA             DEX
00CB:D0 F6          BNE   $00C3

; and show it
00CD:2C 57 C0       BIT   $C057
00D0:2C 52 C0       BIT   $C052
00D3:2C 50 C0       BIT   $C050
00D6:2C 55 C0       BIT   $C055

; and continue elsewhere
00D9:4C 8D B7       JMP   $B78D

                 --^--

And we're back to sector 1.

                 --v--

008D:20 6D BA       JSR   $BA6D
0090:4C 84 9D       JMP   $9D84

                 --^--

Argh.

$BA00 is on T00,S04. $BA69..$BA95 is
unused in some versions of DOS, which
makes it the perfect place to sneak in
a few bytes of... extra code. I don't
like extra. Extra is bad.

And lo, it turns out there is something
very suspicious there: an RWTS swapper!
It starts at $BA69, with a second entry
point at $BA6D (which is what we're
calling during boot).

                 --v--

T00,S04
----------- DISASSEMBLY MODE ----------
0069:A2 6E          LDX   #$6E
006B:D0 0B          BNE   $0078
006D:A2 8A          LDX   #$8A
006F:EA             NOP
0070:D0 06          BNE   $0078
0072:A2 6F          LDX   #$6F
0074:A0 00          LDY   #$00
0076:F0 02          BEQ   $007A
0078:A0 36          LDY   #$36

; encrypt nibble translation table
007A:A9 12          LDA   #$12
007C:5D 00 BA       EOR   $BA00,X
007F:9D 00 BA       STA   $BA00,X

; modify EOR operand (below)
0082:8C 8C BA       STY   $BA8C
0085:A0 3F          LDY   #$3F
0087:BE 29 BA       LDX   $BA29,Y
008A:8A             TXA

; self-modified above, at $BA82
008B:49 FF          EOR   #$FF
008D:9D 00 BA       STA   $BA00,X
0090:88             DEY
0091:10 F4          BPL   $0087
0093:60             RTS

                 --^--

Due to hardware limitations, Apple II
floppy drives are not able to store all
possible data values directly. This
translation table is how DOS converts
the "raw" data on disk to the bytes in
memory (and back). Any alteration of
this translation table would be like
swapping letters of the alphabet to
exchange notes in class. You could
still read it if you knew which letters
had been swapped, but anyone who
intercepted the note and tried to read
it would be baffled.

We're the ones trying to intercept the
note. It's not going to work unless we
can capture one of the translators.

                   ~

               Chapter 2
        In Which We Attempt To
         Capture A Translator


I have two floppy drives, one in slot 6
and the other in slot 5. My "work disk"
(in slot 5) runs Diversi-DOS 64K, which
is compatible with Apple DOS 3.3 but
relocates most of DOS to the language
card on boot. This frees up most of
main memory (only using a single page
at $BF00..$BFFF), which is useful for
loading large files or examining code
that lives in areas typically reserved
for DOS.

[S6,D1=original disk]
[S5,D1=my work disk]

The floppy drive firmware code at $C600
is responsible for aligning the drive
head and reading sector 0 of track 0
into main memory at $0800. Because the
drive can be connected to any slot, the
firmware code can't assume it's loaded
at $C600. If the floppy drive card were
removed from slot 6 and reinstalled in
slot 5, the firmware code would load at
$C500 instead.

To accommodate this, the firmware does
some fancy stack manipulation to detect
where it is in memory (which is a neat
trick, since the 6502 program counter
is not generally accessible). However,
due to space constraints, the detection
code only cares about the lower 4 bits
of the high byte of its own address.

$C600 (or $C500, or anywhere in $Cx00)
is read-only memory. I can't change it,
which means I can't stop it from
transferring control to the boot sector
of the disk once it's in memory. BUT!
The disk firmware code works unmodified
at any address. Any address that ends
with $x600 will boot slot 6, including
$B600, $A600, $9600, &c.

; copy drive firmware to $9600
*9600<C600.C6FFM

; and execute it
*9600G
...reboots slot 6, loads game...

Now then:

]PR#5
...
]CALL -151

*9600<C600.C6FFM

*96F8L

96F8-   4C 01 08    JMP   $0801

That's where the disk controller ROM
code ends and the on-disk code begins.
But $9600 is part of read/write memory.
I can change it at will. So I can
interrupt the boot process after the
drive firmware loads the boot sector
from the disk but before it transfers
control to the disk's bootloader.

; set up callback after boot0 loads the
; rest of track 0 into $B600..$BFFF
96F8-   A9 4C       LDA   #$4C
96FA-   8D 4A 08    STA   $084A
96FD-   A9 0A       LDA   #$0A
96FF-   8D 4B 08    STA   $084B
9702-   A9 97       LDA   #$97
9704-   8D 4C 08    STA   $084C

; start the boot
9707-   4C 01 08    JMP   $0801

; callback is here --
; copy the RWTS to lower memory so it
; survives a reboot
970A-   A2 0A       LDX   #$0A
970C-   A0 00       LDY   #$00
970E-   B9 00 B6    LDA   $B600,Y
9711-   99 00 26    STA   $2600,Y
9714-   C8          INY
9715-   D0 F7       BNE   $970E
9717-   EE 10 97    INC   $9710
971A-   EE 13 97    INC   $9713
971D-   CA          DEX
971E-   D0 EE       BNE   $970E
9720-   AD E8 C0    LDA   $C0E8
9723-   4C 00 C5    JMP   $C500

*9600G
...boots slot 6...
...boots slot 5...

]BSAVE OBJ.B600-BFFF,A$2600

Now we've captured the original disk's
RWTS and the routine (at $BA6D) that
alters the translation table.

                   ~

               Chapter 3
  In Which We Attempt To Use The Disk
      As A Weapon Against Itself


Advanced Demuffin is a cracker's tool
to convert disks to a standard format.
It takes a copy of the original disk's
RWTS (which you must supply), uses that
to read the original, while using its
own copy of a standard RWTS to write
out a copy in a standard format, sector
by sector.

I've included the latest version of
Advanced Demuffin on my work disk.

]BLOAD ADVANCED DEMUFFIN 1.5
]BLOAD OBJ.B600-BFFF,A$2600
]CALL -151

*FE89G FE93G     ; disconnect DOS

*B600<2600.2FFFM ; move RWTS into place

*BA6DG           ; call the routine the
                 ; original disk calls
                 ; before transferring
                 ; control to $9D84

*800G            ; launch Advanced Dem.

[press "C" to convert disk]

["Y" to change default values]

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======================================


INPUT ALL VALUES IN HEX


SECTORS PER TRACK? (13/16) 16

START TRACK: $03        <-- change this
START SECTOR: $00
END TRACK: $22
END SECTOR: $0F

INCREMENT: 1

MAX # OF RETRIES: 0

COPY FROM DRIVE 1
TO DRIVE: 2
=======================================
16SC $03,$00-$22,$0F BY1.0 S6,D1->S6,D2

                 --^--

[S6,D1=original disk]
[S6,D2=blank disk]

And here we go...

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======PRESS ANY KEY TO CONTINUE=======
TRK:   ................................
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0:   ................................
SC1:   ................................
SC2:   ................................
SC3:   ................................
SC4:   ................................
SC5:   ................................
SC6:   ................................
SC7:   ................................
SC8:   ................................
SC9:   ................................
SCA:   ................................
SCB:   ................................
SCC:   ................................
SCD:   ................................
SCE:   ................................
SCF:   ................................
=======================================
16SC $03,$00-$22,$0F BY1.0 S6,D1->S6,D2

                 --^--

Tracks 0-2 are unprotected, so I used
Copy II Plus manual sector copy to copy
them. (Not shown; it's under "bit copy"
if you're looking for it. Just start on
track 0 and end on track 2.)

Now I have a disk that boots but can't
read itself (above track 2), because it
still thinks that track 3 and above use
the altered nibble translation table.
They don't; I just normalized them with
Advanced Demuffin. So I need to disable
that routine. Since there are multiple
entry points ($BA69, $BA6D, and I think
one at $BA72 as well), I should disable
it where they all converge, at $BA7A.

[S6,D1=non-working copy]

T00,S04,$7A: A9 -> 60

]PR#6
...works...

Side B is also protected with the same
altered nibble translation table, on
tracks $01-$22. (Track 0 is standard.)
I used Advanced Demuffin with the same
captured RWTS. There is no additional
code on side B, so no post-demuffin
patches were required.

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1382
------------------EOF------------------
